package com.nononsenseapps.feeder.ui.feed

import android.app.Application
import androidx.lifecycle.*
import androidx.paging.*
import com.gitee.wsl.android.store.statehandle.saveAble
import com.nononsenseapps.feeder.AbstractFeederApplication
import com.nononsenseapps.feeder.ui.R
import com.nononsenseapps.feeder.db.room.*
import com.nononsenseapps.feeder.ui.base.CoroutineScopedViewModel
import com.nononsenseapps.feeder.db.view.PreviewItem
import com.nononsenseapps.feeder.util.FeedViewListType
import com.nononsenseapps.feeder.util.NotificationUtil.cancelNotification
import com.nononsenseapps.feeder.util.PrefUtils
import com.nononsenseapps.feeder.db.*
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.Flow
import org.joda.time.DateTime
import timber.log.Timber
import java.security.InvalidAlgorithmParameterException
import java.util.*

private const val PAGE_SIZE = 30

class FeedItemsViewModel(application: Application , state: SavedStateHandle) :  CoroutineScopedViewModel(application) {

     var feedId  by state.saveAble( ARG.FEED_ID, defaultValue = ID_UNSET)
     var feedGroup by state.saveAble( ARG.TAG, defaultValue = "" )

    private val liveOnlyUnread =  MutableLiveData(PrefUtils.isShowOnlyUnread(application))

    val refreshAble = state.getLiveData(UIState.FeedItemList.FEED_ITEM_LIST_REFRESH_ABLE,true)

    val feedViewListType = MutableLiveData(PrefUtils.getFeedViewListType(context = application))

    var loadingMore = false

    var firstInit = true

    val isSystemFeed :Boolean
        get() = feedId.isSystemFeed()

    val isReadBackRefreshAble:Boolean
        get() = currentModel in mutableListOf(ItemListViewModel.GROUP,ItemListViewModel.FEED,ItemListViewModel.ALL_FEED)

    var feedItemFilterTags by state.saveAble( UIState.FeedItemList.FEED_ITEM_FILTER_TAGS, defaultValue = "" )

    var beforeModel by state.saveAble(UIState.FeedItemList.FEED_ITEM_BEFORE_LIST_MODEL, ItemListViewModel.UN_DEFAULT)

    var currentModel by state.saveAble(UIState.FeedItemList.FEED_ITEM_LIST_MODEL, ItemListViewModel.UN_DEFAULT)

    var searchText by state.saveAble<String>(UIState.FeedItemList.FEED_ITEM_LIST_SEARCH_TEXT)

    var featureId by state.saveAble<Long>(UIState.FeedItemList.FEED_ITEM_LIST_FEATURE_ID,ID_UNSET)

    val displayTitle = state.getLiveData(UIState.FeedItemList.FEED_ITEM_LIST_TITLE,"")

    private var searchStartDay by state.saveAble<Date>(UIState.FeedItemList.FEED_ITEM_LIST_SEARCH_START_DATE)
    private var searchEndDate by state.saveAble<Date>(UIState.FeedItemList.FEED_ITEM_LIST_SEARCH_END_DATE)

    val pagingConfig = PagingConfig(pageSize = PAGE_SIZE, initialLoadSize = 30,enablePlaceholders=true)

    var lastViewFeedItemId by state.saveAble<Long>(UIState.FeedItemList.FEED_ITEM_LIST_LAST_VIEW_ITEM_ID,0)

    var startReadTime by state.saveAble<Date>(UIState.FeedItemList.FEED_ITEM_LIST_START_DATE,Calendar.getInstance().time)

    var blackSynced by state.saveAble<Boolean>(UIState.FeedItemList.FEED_ITEM_LIST_LAST_VIEW_ITEM_ID,false)

    var readTypeChanged by state.saveAble<Boolean>(UIState.FeedItemList.FEED_ITEM_LIST_READ_TYPE_CHANGED,false)

    val mustScrollToTop:Boolean
        get() = currentModel != beforeModel || (currentModel == beforeModel && readTypeChanged)

    //var currentItemList: Flow<PagingData<PreviewItem>> = flowOf(PagingData.empty())

    fun getCurrentPagingDataFlow(): Flow<PagingData<PreviewItem>> {
        return Pager(pagingConfig){
            when(currentModel){
                ItemListViewModel.GROUP -> {
                    if(liveOnlyUnread.value == true)
                        feedItemDao.loadLiveUnreadPreviews(tag = feedGroup,startReadTime=DateTime(startReadTime.time))
                    else
                        feedItemDao.loadLivePreviews(tag = feedGroup,startReadTime=DateTime(startReadTime.time))
                }
                ItemListViewModel.TAG -> {
                    val tags=feedItemFilterTags.split(",")
                    when(tags.size) {
                        2 -> feedItemDao.loadLiveUnreadPreviewsWithTags(tags[0],tags[1],liveOnlyUnread.value!!)
                        3 -> feedItemDao.loadLiveUnreadPreviewsWithTags(tags[0],tags[1],tags[2],liveOnlyUnread.value!!)
                        4 -> feedItemDao.loadLiveUnreadPreviewsWithTags(tags[0],tags[1],tags[2],tags[3],liveOnlyUnread.value!!)
                        else -> {
                            if( liveOnlyUnread.value == true )
                                feedItemDao.loadLiveUnreadPreviewsWithTag(tags[0],liveOnlyUnread.value!!)
                            else
                                feedItemDao.loadLivePreviewsWithTag(tags[0])
                        }
                    }
                }
                ItemListViewModel.FEATURE -> {
                    feedItemDao.loadLivePreviewsWithFeature(featureId)
                }
                ItemListViewModel.HISTORY -> {
                    feedItemDao.loadReadHistoryPreviews()
                }
                ItemListViewModel.SEARCH -> {
                    if(liveOnlyUnread.value == true)
                        feedItemDao.loadLiveUnreadPreviewsWithSearch(searchText?:"")
                    else
                        feedItemDao.loadLivePreviewsWithSearch(searchText?:"")
                }
                ItemListViewModel.DATE_TO_DATE -> {
                    val startTime=if(searchStartDay!=null) DateTime(searchStartDay!!.time) else DateTime()
                    val endTime=if(searchEndDate!=null) DateTime(searchEndDate!!.time) else DateTime()
                    if(liveOnlyUnread.value == true) {
                        if(isSystemFeed){
                            feedItemDao.queryByDateWithFeedAndUnRead(feedId,startTime, endTime)
                        }else
                            feedItemDao.queryByDateWithUnRead(startTime, endTime)
                    }else {
                        if(isSystemFeed){
                            feedItemDao.queryByDateWithFeed(feedId,startTime, endTime)
                        }else
                            feedItemDao.queryByDate(startTime, endTime)
                    }
                }
                ItemListViewModel.DOWNLOADER ->{
                    if(liveOnlyUnread.value==true) {
                        feedItemDao.loadLiveDownLoadUnreadPreviews()
                    }else
                        feedItemDao.loadLiveDownLoadPreviews()
                }
                ItemListViewModel.FEED -> {
                    if(liveOnlyUnread.value == true) {
                        feedItemDao.loadLiveUnreadPreviews(feedId = feedId,startReadTime=DateTime(startReadTime.time))
                    }else
                        feedItemDao.loadLivePreviews(feedId = feedId,startReadTime=DateTime(startReadTime.time))
                }
                ItemListViewModel.ALL_FEED -> {
                    if(liveOnlyUnread.value == true)
                        feedItemDao.loadLiveUnreadPreviews(startReadTime=DateTime(startReadTime.time))
                    else
                        feedItemDao.loadLivePreviews(startReadTime=DateTime(startReadTime.time))
                }
                else->{
                   Timber.d("getCurrentPagingDataFlow itemListViewModel:$currentModel,saved feedId:$feedId,feedGroup:$feedGroup")
                   throw InvalidAlgorithmParameterException()
                }
            }
        }.flow.cachedIn(viewModelScope)
    }

    fun initArguments(id: Long, feedTag: String?) {
        Timber.d("feedItemsViewmodel initArguments feedId:$id,feedGroup:$feedTag")
        if(id != ID_UNSET) {
            this.feedId = id

        } else if(feedTag.isNullOrEmpty())
                  this.feedId = PrefUtils.getLastOpenFeedId(getApplication())
               else
                  this.feedId = ID_ALL_FEEDS

        if(feedTag!=null) {
            this.feedGroup = feedTag
        }else
            this.feedGroup = PrefUtils.getLastOpenFeedTag(getApplication())?:""

        Timber.d("feedItemsViewmodel init use saved feedId:$feedId,feedGroup:$feedGroup")

        when {
            feedId.isFeed() -> {
                val isExitsFeed= runBlocking(Dispatchers.IO) {
                    feedDao.isExitsFeed(feedId)
                }
                if(!isExitsFeed) {
                    feedId = ID_ALL_FEEDS
                    changeAllFeedToRead()
                    return
                }
                val feedName = runBlocking(Dispatchers.IO) {
                    feedDao.getFeedName(feedId)
                }
                changeFeedToRead("Feed")
                setDisplayTitle(feedName)
            }

            feedGroup.isNotEmpty() -> changeGroupToRead(feedGroup)

            feedId.isAllFeed() -> changeAllFeedToRead()

            else -> {
                Timber.d("feedItemsViewmodel init no catch type,feedId:$feedId,feedGroup:$feedGroup")
                feedId = ID_ALL_FEEDS
                changeAllFeedToRead()
            }
        }
    }

    fun isOnlyUnread() = liveOnlyUnread.value!!

    fun markAllAsDelete() = viewModelScope.launch(Dispatchers.IO) {
         feedItemDao.markAllAsDelete()
    }

    fun markAllAsDelete(ids:List<Long>) = viewModelScope.launch(Dispatchers.IO) {
        feedItemDao.markAllAsDelete(ids)
    }

    fun markAllAsUnRead() = viewModelScope.launch(Dispatchers.IO) {
        feedItemDao.markAllAsUnRead()
    }

    fun markAllAsUnRead(ids:List<Long>) = viewModelScope.launch(Dispatchers.IO) {
        feedItemDao.markAllAsUnRead(ids)
    }

    fun markAllAsFavorite(ids: List<Long>)  = viewModelScope.launch(Dispatchers.IO) {
        feedItemDao.markAllAsFavorite(ids)
    }

    fun markFeedItemAsFavorite(itemId: Long, tag: String)  = viewModelScope.launch(Dispatchers.IO) {
        feedItemDao.markAsFavorite(itemId,tag)
    }

    fun markAllFeedItemAsRead()=viewModelScope.launch(Dispatchers.IO) {
        feedItemDao.markAllAsRead()
    }

    fun markCurrentListItemAsRead() = viewModelScope.launch(Dispatchers.IO) {
        when {
            feedId.isAllFeed() -> feedItemDao.markAllAsRead()
            feedId.isFeed() -> feedItemDao.markAllAsRead(feedId)
            feedGroup.isNotEmpty() -> feedItemDao.markAllAsRead(feedGroup)
        }
    }

    fun markCurrentListItemAsRead(ids:List<Long>) = viewModelScope.launch(Dispatchers.IO) {
        feedItemDao.markAllAsRead(ids)
    }

    /**
     * Already called within a coroutine scope, do not launch another to keep ordering guarantees
     */
    fun markFeedItemAsNotified() = viewModelScope.launch(Dispatchers.IO) {
        when {
            feedId.isSystemFeed() -> {}
            feedId.isFeed() -> feedItemDao.markAsNotified(feedId)
            feedId.isAllFeed() -> feedItemDao.markAllAsNotified()
            feedGroup.isNotEmpty() -> feedItemDao.markTagAsNotified(feedGroup)
        }
    }

    fun markFeedItemAsNotified(ids: List<Long>) = viewModelScope.launch(Dispatchers.IO) {
        feedItemDao.markAsNotified(ids)
    }

    fun toggleReadState(feedItem: PreviewItem) = viewModelScope.launch(Dispatchers.IO) {
        feedItemDao.markAsRead(feedItem.id, unread = !feedItem.unread)
        cancelNotification(getApplication(), feedItem.id)
    }

    fun changeFeedViewListType(type:FeedViewListType){
        feedViewListType.value=type
        if(feedId.isFeed()){
            val showType=when(type){
                FeedViewListType.NORMAL -> 1
                FeedViewListType.PICTURE -> 2
                FeedViewListType.MINI -> 3
                FeedViewListType.TREE -> 90
                else -> 0
            }
            viewModelScope.launch(Dispatchers.IO) {
                feedDao.setFeedViewListType(feedId,showType)
            }
        } else
            PrefUtils.setFeedViewListType(context = getApplication(),type = type)
    }

    private fun changeFeedViewListTypeFromFeed(id:Long) = viewModelScope.launch(Dispatchers.IO){
        feedDao.findFeedViewListType(id).let {
            val defTypeValue = when (it) {
                1 -> FeedViewListType.NORMAL
                2 -> FeedViewListType.PICTURE
                3 -> FeedViewListType.MINI
                90 -> FeedViewListType.TREE
                else -> feedViewListType.value
            }
            if (feedViewListType.value != defTypeValue)
                viewModelScope.launch(Dispatchers.Main){
                    feedViewListType.value = defTypeValue
                }
        }
    }

    private fun useGlobalFeedViewListType(){
        val defTypeValue=PrefUtils.getFeedViewListType(context = getApplication())
        if(feedViewListType.value != defTypeValue)
            feedViewListType.value = defTypeValue
    }

    private fun initFeedViewListType(){
        if(feedId.isFeed())
            changeFeedViewListTypeFromFeed(feedId)
        else
            useGlobalFeedViewListType()
    }

    fun loadingMore(){
        loadingMore=true
        viewModelScope.launch {
            delay(1000)
            loadingMore=false
        }
    }

    fun isSyncAble():Boolean {
       return when(currentModel){
           ItemListViewModel.GROUP,ItemListViewModel.ALL_FEED -> true
           ItemListViewModel.FEED -> feedId > ID_UNSET
           else -> false
        }
    }

    fun setDisplayTitle(title:String){
        Timber.d("setDisplayTitle:$title")
        displayTitle.value = title
    }

    fun checkReFlushAble(){
        refreshAble.value = isSyncAble()
    }

    fun markFeedItemAsUnRead(id: Long) = viewModelScope.launch(Dispatchers.IO) {
        feedItemDao.markAsRead(id,unread = true)
    }

    fun markFeedItemAttentionLevel(id: Long, attentionLevel: Int) = viewModelScope.launch(Dispatchers.IO) {
        feedItemDao.markItemAttentionLevel(id, attentionLevel)
    }

    fun toggleOnlyUnread() {
        val unread = !liveOnlyUnread.value!!
        liveOnlyUnread.value=unread
        PrefUtils.setPrefShowOnlyUnread(getApplication() ,unread)
    }

    fun filterWithAllFeed(){
        if(this.feedId != ID_ALL_FEEDS)
            readTypeChanged = true
        this.feedId = ID_ALL_FEEDS
        this.feedGroup = ""
        PrefUtils.setLastOpenFeed(context =getApplication(),this.feedId,this.feedGroup)
        changeAllFeedToRead()
    }

    fun filterWithFeed(id:Long,title: String) {
        if(this.feedId != id)
            readTypeChanged = true
        this.feedId = id
        this.feedGroup = ""
        PrefUtils.setLastOpenFeed(context =getApplication(),this.feedId,this.feedGroup)
        changeFeedToRead(title)
    }


    fun filterWithFeedGroup(newTag:String) {
        if(this.feedGroup != newTag)
            readTypeChanged = true
        this.feedId = ID_UNSET
        this.feedGroup = newTag
        PrefUtils.setLastOpenFeed(context =getApplication(),this.feedId,this.feedGroup)
        changeGroupToRead(feedGroup)
    }

    fun filterWithTags(tags:List<String>){
        feedItemFilterTags = tags.joinToString(",")
        changeTagToRead(getApplication<AbstractFeederApplication>().getString(R.string.tag)+":" +  feedItemFilterTags )
        readTypeChanged = true
    }

    fun filterByDate(firstDay: Date, lastDay: Date) {
        if(this.searchStartDay!=firstDay || this.searchEndDate != lastDay)
            readTypeChanged = true
        this.searchStartDay = firstDay
        this.searchEndDate = lastDay
        changeQueryByDateToRead(firstDay, lastDay)
    }

    fun filterWithFeature(featureId: Long, title: String) {
        if(this.featureId != featureId)
            readTypeChanged = true
        this.featureId=featureId
        changeFeatureToRead(title)
    }

    fun filterWithReadHistory() {
        changeHistoryToRead()
    }

    fun filterWithDownloadHistory() {
        changeDownLoadToRead()
    }

    fun filterWithSearchText(searchText:String){
        this.searchText = searchText
        changeSearchToRead(searchText)
        useGlobalFeedViewListType()
    }

    fun desableSearchModel(){
        currentModel = beforeModel
        checkReFlushAble()
    }

    // state Change Action
    fun changeAllFeedToRead(){
        changeListViewModel(ItemListViewModel.ALL_FEED)
        setDisplayTitle(getApplication<AbstractFeederApplication>().getString(R.string.all_feeds))
    }

    fun changeFeedToRead(title:String){
        changeListViewModel(ItemListViewModel.FEED)
        setDisplayTitle(title)
        initFeedViewListType()
    }

    private fun changeFeatureToRead(title: String) {
        changeListViewModel(ItemListViewModel.FEATURE)
        setDisplayTitle(title)
        initFeedViewListType()
    }

    fun changeGroupToRead(group:String){
        changeListViewModel(ItemListViewModel.GROUP)
        setDisplayTitle(group)
    }

    fun changeSearchToRead(searchText:String){
        changeListViewModel(ItemListViewModel.SEARCH)
        setDisplayTitle(searchText)
    }

    fun changeHistoryToRead(){
        changeListViewModel(ItemListViewModel.HISTORY)
        setDisplayTitle(getApplication<AbstractFeederApplication>().getString(R.string.read_history))
    }

    fun changeDownLoadToRead(){
        changeListViewModel(ItemListViewModel.DOWNLOADER)
        setDisplayTitle(getApplication<AbstractFeederApplication>().getString(R.string.download_enclosure))
    }

    private fun changeTagToRead(tag:String){
        changeListViewModel(ItemListViewModel.TAG)
        setDisplayTitle(tag)
    }

    private fun changeQueryByDateToRead(firstDay: Date?, lastDay: Date?){
        changeListViewModel(ItemListViewModel.DATE_TO_DATE)
        setDisplayTitle("$firstDay")
    }

    private fun changeListViewModel(model:ItemListViewModel){
        Timber.d("changeListViewModel to: $model from :$currentModel")
        if(currentModel != ItemListViewModel.SEARCH)
           beforeModel = currentModel
        currentModel = model
        checkReFlushAble()
    }

    fun setFeedViewListTypeTreeName(feedId: Long, treeId: String)=viewModelScope.launch(Dispatchers.IO) {
        feedDao.setFeedViewListTypeTreeName(feedId,treeId)
    }

    fun listCurrentFeedItems():Flow<List<PreviewItem>> {
        return feedItemDao.loadFeedLivePreviews(feedId)
    }
}

/*class FeedItemsViewModelFactory : ViewModelProvider.NewInstanceFactory() {
    override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
        val application = checkNotNull(extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY])
        return FeedItemsViewModel(application,extras.createSavedStateHandle()) as T
    }
}

fun Fragment.getFeedItemsViewModel(): FeedItemsViewModel {
    return ViewModelProvider(this,FeedItemsViewModelFactory())[FeedItemsViewModel::class.java]
}*/

enum class ItemListViewModel{
    UN_DEFAULT,FEED,GROUP,TAG,SEARCH,HISTORY,ALL_FEED,DATE_TO_DATE,DOWNLOADER,FEATURE
}